/***************************************************************************
 *   Copyright (C) 2009 by Zheng Yonglei
 *   zhengyonglei@gmail.com   *
 *                                                                         *
 *   This program is free software; you can redistribute it and/or modify  *
 *   it under the terms of the GNU General Public License as published by  *
 *   the Free Software Foundation; either version 2 of the License, or     *
 *   (at your option) any later version.                                   *
 *                                                                         *
 *   This program is distributed in the hope that it will be useful,       *
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
 *   GNU General Public License for more details.                          *
 *                                                                         *
 *   You should have received a copy of the GNU General Public License     *
 *   along with this program; if not, write to the                         *
 *   Free Software Foundation, Inc.,                                       *
 *   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             *
 ***************************************************************************/

#include <QTextCodec>

#include "Dictionary.h"
#include "Logger.h"
#include "Options.h"

Dictionary::Dictionary(QWidget *parent)
    : QWidget(parent),
    mArticleWidthRate(0.75f)
{
	ui.setupUi(this);

	QGridLayout* toolBarGrid = new QGridLayout(ui.dictToolBarWidget);
	toolBarGrid->setContentsMargins(0,0, 0, 0);
	mToolBar = new QToolBar(this);
	toolBarGrid->addWidget(mToolBar);

	//Setup navigation actions
	mShowWordList = new QAction(QIcon(":/pixmaps/kghostview.png"), tr("Show word list"), this);
	mShowWordList->setCheckable(true);
	QObject::connect(mShowWordList, SIGNAL(toggled(bool)),this,SLOT(setShowWordList(bool)));
	mShowWordList->setChecked(DICT_OPT.mShowWordList);
	setShowWordList(DICT_OPT.mShowWordList);

	mShowWordNav = new QAction(QIcon(":/pixmaps/view_tree-256.png"), tr("Show article navigator"), this);
	mShowWordNav->setCheckable(true);
	QObject::connect(mShowWordNav, SIGNAL(toggled(bool)),this,SLOT(setShowWordNav(bool)));
	mShowWordNav->setChecked(DICT_OPT.mShowWordNav);
	setShowWordNav(DICT_OPT.mShowWordNav);

	mToolBar->addAction(mShowWordList);
	mToolBar->addAction(mShowWordNav);

	//Setup search bar
	mSearchWordComboBox = new QComboBox(this);
	mSearchWordComboBox->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred);
	mSearchWordComboBox->setEditable(true);
	mSearchWordComboBox->setFont(this->font());
	mSearchWordComboBox->setInsertPolicy(QComboBox::InsertAtTop);
	QObject::connect(mSearchWordComboBox, SIGNAL(editTextChanged(QString)), this, SLOT(inputChanged(QString)));
	QObject::connect(mSearchWordComboBox->lineEdit(), SIGNAL(returnPressed()), this, SLOT(returnPressed()));
	QObject::connect(ui.wordList, SIGNAL(itemSelectionChanged()), this, SLOT(itemSelectionChanged()));
	mToolBar->addWidget(mSearchWordComboBox);

	//Setup lookup action
	mLookupWordAction = new QAction(tr("Find Word"), this);
	QObject::connect(mLookupWordAction,SIGNAL(activated()),this,SLOT(addHistoryItem()));

	mLookupMenu = new QMenu(this);
	mSelectNormalLookup = new QAction(QIcon(":/pixmaps/find-256.png"), tr("Normal lookup"), this);
	mSelectFuzzyLookup = new QAction(QIcon(":/pixmaps/wifi.png"), tr("Fuzzy lookup"), this);
	QObject::connect(mSelectNormalLookup,SIGNAL(activated()),this,SLOT(selectNormalLookup()));
	QObject::connect(mSelectFuzzyLookup,SIGNAL(activated()),this,SLOT(selectFuzzyLookup()));
	mLookupMenu->addAction(mSelectNormalLookup);
	mLookupMenu->addAction(mSelectFuzzyLookup);

	mLookupWordAction->setMenu(mLookupMenu);
	switch(DICT_OPT.mLookupMode)
	{
	case LM_Normal:
		mLookupWordAction->setIcon(mSelectNormalLookup->icon()); break;
	case LM_Fuzzy:
		mLookupWordAction->setIcon(mSelectFuzzyLookup->icon()); break;
	default:
		LOG<<"Invalid LookupMode:"<<DICT_OPT.mLookupMode<<endl;
		assert(0);
	}

	mToolBar->addAction(mLookupWordAction);

	//Setup setting action
	settingAction = new QAction(QIcon(":/pixmaps/configure.png"), tr("Configure my dictionary"), this);
	QObject::connect(settingAction,SIGNAL(activated()),this,SLOT(showSettingDialog()));
	mToolBar->addAction(settingAction);

	//Setup speak action
	mSpeakAction = new QAction(QIcon(":/pixmaps/speaker.png"), tr("Speak"), this);
	QObject::connect(mSpeakAction, SIGNAL(triggered()), this, SLOT(speak()));
	mToolBar->addAction(mSpeakAction);

	mShowInstruction = new QAction(QIcon(":/pixmaps/dialog-information.png"), tr("Show instruction"), this);
	QObject::connect(mShowInstruction, SIGNAL(triggered()), this, SLOT(insertIntroduction()));
	mToolBar->addAction(mShowInstruction);

	//adjust splitter
	int width = this->width();
	QList<int> sizes;
	sizes<<width*(1 - mArticleWidthRate)<<width*mArticleWidthRate;
	ui.splitter_2->setSizes(sizes);

	//set navigator view
	ui.wordNavTree->setHeaderHidden(true);
	QObject::connect(ui.wordNavTree, SIGNAL(itemClicked(QTreeWidgetItem*, int)), this, SLOT(itemActivated(QTreeWidgetItem*, int)));

	ui.wordList->setWindowTitle(tr("word list"));
	ui.wordNavTree->setWindowTitle(tr("article tree"));

	//set textbrowser
	ui.wordBrowserDisplay->setOpenLinks(false);

	QObject::connect(&mTimer,SIGNAL(timeout()),this,SLOT(lookupTimeout()));
	mTimer.setSingleShot(true);

	insertIntroduction();
}

Dictionary::~Dictionary()
{
}

void Dictionary::setInput(QString key)
{
	mSearchWordComboBox->setEditText(key);
}

void Dictionary::setDisplayFont(QFont& font)
{
	ui.wordBrowserDisplay->setFont(font);
	ui.wordList->setFont(font);
	ui.wordNavTree->setFont(font);
}

void Dictionary::inputChanged(QString text)
{
	text = text.simplified();
	if (text.isEmpty())
		return;

	if (mSearchWordComboBox->currentText() != text)
		mSearchWordComboBox->setEditText(text);
	mTimer.start(DICT_OPT.mLookupDelayMS);
}

void Dictionary::lookupTimeout()
{
	if (mSearchWordComboBox->currentText().isEmpty())
	{
		ui.wordList->clear();
		ui.wordNavTree->clear();
		insertIntroduction();
		return;
	}

	switch(DICT_OPT.mLookupMode)
	{
	case LM_Normal:
		lookupNormal(); break;
	case LM_Fuzzy:
		lookupFuzzy(); break;
	default:
		LOG<<"Invalid LookupMode:"<<DICT_OPT.mLookupMode<<endl;
		assert(0);
	}

}

void Dictionary::selectNormalLookup()
{
	DICT_OPT.mLookupMode = LM_Normal;
	mLookupWordAction->setIcon(mSelectNormalLookup->icon());
	lookupNormal();
}

void Dictionary::selectFuzzyLookup()
{
	DICT_OPT.mLookupMode = LM_Fuzzy;
	mLookupWordAction->setIcon(mSelectFuzzyLookup->icon());
	lookupFuzzy();
}

void Dictionary::setShowWordList(bool arg)
{
	DICT_OPT.mShowWordList = arg;
	ui.wordList->setVisible(arg);
}

void Dictionary::setShowWordNav(bool arg)
{
	DICT_OPT.mShowWordNav = arg;
	ui.wordNavTree->setVisible(arg);
}

void Dictionary::addHistoryItem()
{
	if (!mSearchWordComboBox->currentText().isEmpty())
		mSearchWordComboBox->insertItem(0, mSearchWordComboBox->currentText());

	lookupTimeout();
}

void Dictionary::lookupNormal()
{
	mCurKey = mSearchWordComboBox->currentText();
	mCurKey = mCurKey.simplified();
	if (!mCurKey.isEmpty())
	{
		int wordListMax = 30;
		setWordList(DM.findKeysBeginWith(mCurKey, wordListMax));
	}
}

void Dictionary::lookupFuzzy()
{
	mCurKey = mSearchWordComboBox->currentText();
	mCurKey = mCurKey.simplified();
	if (!mCurKey.isEmpty())
	{
		int wordListMax = 30;
		int distanceMax = 3;
		setWordList(DM.findKeysFuzzyWith(mCurKey, wordListMax, distanceMax));
	}
}

void Dictionary::lookupWord(QString Word)
{
	if (mLastQueryKey == Word)
		return;

	mLastQueryKey = Word;
	clearHtml();
	QString comment;
	if(!Word.isEmpty())
	{
		DM.lookup(Word, DICT_OPT.mQueryDictList);

		ui.wordNavTree->clear();
		mCurItem = 0;
		mNavItemListMap.clear();
		mNavItemListVector.clear();

		for (int i=0; i<DICT_OPT.mQueryDictList.size(); i++)
		{
			map<QString, QString>* articles = DM.getExplanationMap(DICT_OPT.mQueryDictList[i]);
			if (!articles)
				continue;

			QTreeWidgetItem* dictItem = new QTreeWidgetItem(ui.wordNavTree, QStringList(DICT_OPT.mQueryDictList[i]));
			comment = QString("<!--Dict[%1]-->").arg(DICT_OPT.mQueryDictList[i]);
			insertHtml(comment);
			//bool isValid = false;
			for (map<QString, QString>::iterator ar_it = articles->begin();
					ar_it != articles->end();
					ar_it++)
			{
				QString key(ar_it->first);
				key = key.simplified();
				if (!key.isEmpty())
				{
					QTreeWidgetItem* keyItem = new QTreeWidgetItem(QStringList(key));
					dictItem->addChild(keyItem);
					mNavItemListMap[keyItem]=ui.wordBrowserDisplay->textCursor().position();
					mNavItemListVector.push_back(keyItem);
					comment = QString("<!--Key[%1]-->").arg(key);
					insertHtml(comment);
					insertHtml(QString("<!--item[%1]-->").arg(mNavItemListVector.size()-1));
					insertHtml(QString("<FONT style=\"BACKGROUND-COLOR: %1\">").arg(DICT_OPT.mMainShadingColor.name()));
					insertHtml(ar_it->second);
					insertHtml("</FONT>");
				}
			}
			insertHtml("<BR>");
		}
	}

	ui.wordNavTree->setFont(ui.wordBrowserDisplay->font());
	ui.wordNavTree->expandAll();
}

void Dictionary::setWordList(QStringList wordList)
{
	ui.wordList->clear();
	ui.wordNavTree->clear();
	clearHtml();
	for (int i=0; i<wordList.size(); i++)
	{
		QListWidgetItem* item = new QListWidgetItem(wordList[i], ui.wordList);
		if (mCurKey == wordList[i])
			item->setSelected(true);
	}

	if (ui.wordList->selectedItems().size() == 0 && ui.wordList->count() != 0)
		ui.wordList->item(0)->setSelected(true);
}

void Dictionary::itemSelectionChanged()
{
	QList<QListWidgetItem *> items = ui.wordList->selectedItems();
	if (items.size()>0)
		lookupWord(items[0]->text());
}

void Dictionary::itemActivated(QTreeWidgetItem* item, int column)
{
	map<QTreeWidgetItem*, int>::iterator it_it = mNavItemListMap.find(item);
	if (it_it != mNavItemListMap.end())
	{
		ui.wordNavTree->scrollToItem(item);
		if (mCurItem != mNavItemListVector.indexOf(item))
			mCurItem = mNavItemListVector.indexOf(item);

		if (mNavItemListMap[item] >= 0 )
		{
			//set backgroud color
			QString content = mHtml;
			//content.replace("<FONT style=\"BACKGROUND-COLOR: #ccff66", "<FONT style=\"BACKGROUND-COLOR: #ffffff");
			int pos = content.indexOf(QString("<!--item[%1]-->").arg(mCurItem));
			if (pos != -1)
			{
				pos += (QString("<!--item[%1]--><FONT style=\"BACKGROUND-COLOR: ").arg(mCurItem).size());
				content.replace(pos, DICT_OPT.mMainShadingColor.name().size(), DICT_OPT.mSelectShadingColor.name());
			}
			ui.wordBrowserDisplay->clear();
			ui.wordBrowserDisplay->insertHtml(content);

			QTextCursor cursor = ui.wordBrowserDisplay->textCursor();
			cursor.setPosition(mNavItemListMap[item]);
			int buttonLine = ui.wordBrowserDisplay->verticalScrollBar()->maximumHeight();
			ui.wordBrowserDisplay->verticalScrollBar()->setValue(buttonLine);
			ui.wordBrowserDisplay->setTextCursor(cursor);
		}
	}
}

//revise needed
void Dictionary::speak(QString key)
{
	LOG<<"speak("<<key<<")"<<endl;
	QDir dir(DICT_OPT.speakLibPath);
	QString Word;
	if (key.isEmpty())
		Word = mCurKey + QString(".wav");
	else
		Word = key + QString(".wav");
	QString capital = Word.left(1);
	dir.cd(capital);

	QString soundFileName = dir.absolutePath() + QString("/") + Word;
	if (!DICT_OPT.useExternalPlayer)
	{
		QSound::play(soundFileName);
	}
	else if (!DICT_OPT.playSoundCommand.isEmpty())
	{
		QString program = DICT_OPT.playSoundCommand;
		QStringList arguments;
		arguments << soundFileName;

		QProcess *myProcess = new QProcess(this);
		myProcess->start(program, arguments);
	}
	else
		LOG<<"Invalid setting."<<endl;
}

void Dictionary::insertHtml(QString html)
{
	ui.wordBrowserDisplay->insertHtml(html);
	mHtml.append(html);
}

void Dictionary::clearHtml()
{
	ui.wordBrowserDisplay->clear();
	mHtml.clear();
}

void Dictionary::showSettingDialog()
{
	DictSettingDialog dictSettingDialog;
	dictSettingDialog.exec();
	DM.setKeyMap(DICT_OPT.mQueryDictList);
}

void Dictionary::insertIntroduction()
{
	clearHtml();
	QString instruction = tr(
	"This is a dictionary for ReciteGREWords.\n"
	"Enter the word into the TextEdit above to look up its meaning.\n"
	"Manage dictionaries in Dictinary Setting dialog. Please note that new added dictionaries (copy to ./data/dictionary) must be checked in this dialog so that they can be used for query. \n"
	"\n"
	"ReciteGREWords uses dictionaries provided by other opensource projects, such as XDXF(http://xdxf.sourceforge.net/) and stardict(http://stardict.sourceforge.net/).\n"
	"\n"
	"Instruction: How to use XDXF dictionaries in ReciteGREWords\n"
	"1. Download XDXF dictionaries in http://xdxf.revdanica.com/down/ (eg. Merriam-Webster's Collegiate dictionary from http://downloads.sourceforge.net/xdxf/comn_dls03_mwc.tar.bz2)\n"
	"2. Unzip the package. You will find a file named \"dict.xdxf\" which is a dictionary in xml format. \n"
	"3. Run ReciteGREWords and click menu \"Tools\" --> \"Dictionary Maker\" and open DictMaker.\n"
	"4. Click button \"Select file to convert\" and select the XDXF dictionary file (*.xdxf) you downloaded.\n"
	"5. Please be patient after you click \"Open\" button in the file dialog. It could take a while to prepare for the conversion. \n"
	"6. If the XDXF dictionary is parsed successfully, you will see its Name, Description and Word count in the TextBrowser. Click \"Dictionary database\" button to name your new dictionary database. \n"
	"7. Click \"Convert\" to convert XDXF dictionary file to dictionary database file used in ReciteGREWords.\n"
	"8. Restart ReciteGREWords and enjoy. \n"
	"\n"
	"Instruction: How to use stardict dictionaries in ReciteGREWords\n"
	"The converting process is similar to converting the XDXF dictionaries. However, it has two differences:\n"
	"1. The dictionary file must be converted to TXT format by StarDict-Editor in advance.\n"
	"2. Please make sure that .ifo file and .txt file are in the same directory and have the same file name except file extensions. Select .ifo file when select the file to convert. \n"
	"\n"
	"Important: The dictionary database files used in ReciteGREWords must have extension \"db\", and locate in \"./data/dictionary\"."
	"\n"
	);

	ui.wordBrowserDisplay->insertPlainText(instruction);
	ui.wordBrowserDisplay->insertHtml("<br>" + tr("For more information please visit") + " <B>http://code.google.com/p/recitegrewords/</B><BR>");

	ui.wordList->clear();
	ui.wordList->addItem("NULL");
	ui.wordNavTree->clear();
	ui.wordNavTree->addTopLevelItem(new QTreeWidgetItem(ui.wordNavTree, QStringList("NULL")));
}

void Dictionary::returnPressed()
{
	mSearchWordComboBox->lineEdit()->selectAll();
}
